import axios, { AxiosError, AxiosRequestConfig, AxiosResponse, Method } from 'axios'
const qs = require('qs')
import { HttpConfig, HttpResult, PendingType, tokenFunction } from './type';
import { message } from 'ant-design-vue';
import { errorConfig } from './errorConfig';

class HttpRequest {
  protected loading: null;
  private axiosRequestNumber: number;
  protected config: HttpConfig
  // 取消重复请求
  protected pending: Array<PendingType> = [];
  protected CancelToken = axios.CancelToken;
  constructor(config: HttpConfig) {
    this.loading = null
    this.axiosRequestNumber = 0
    /**
     * axios常用配置
     */
    this.config = {
      baseURL: '',
      timeout: 10000,
      proxyUrl: '/api',
      token: null,
      successCode: ['SYS200'],
      noAuthCode: ['SYS401', 'SYS402'],
      notAuthUrls: [],
      callBackNoAuth: null,
      callBackError: null,
      startLoading: null,
      closeLoading: null
    }
    // 合并config配置
    for (const item in this.config) {
      // 只有传入的config有值时才会进行覆盖
      if (config[item]) {
        this.config[item] = config[item]
      }
    }
    axios.defaults.timeout = this.config.timeout
    if (process.env.NODE_ENV === 'development') {
      axios.defaults.baseURL = this.config.proxyUrl
    } else if (process.env.NODE_ENV === 'production') {
      axios.defaults.baseURL = this.config.baseURL // 生产环境
    }
    axios.interceptors.request.use(
      (config) => {
        this.removePending(config);
        config.cancelToken = new this.CancelToken((c) => {
          this.pending.push({
            url: config.url,
            method: config.method,
            params: config.params,
            data: config.data,
            cancel: c
          });
        });
        return config;
      },
      (err: AxiosError) => {
        return Promise.reject(err)
      }
    )
  }

  // 移除重复请求
  removePending(config: AxiosRequestConfig) {
    for (const key in this.pending) {
      const item: number = +key;
      const list: PendingType = this.pending[key];
      // 当前请求在数组中存在时执行函数体
      if (list.url === config.url
        && list.method === config.method
        && JSON.stringify(list.params) === JSON.stringify(config.params)
        && JSON.stringify(list.data) === JSON.stringify(config.data)
      ) {
        // 执行取消操作
        list.cancel('操作太频繁，请稍后再试');
        // 从数组中移除记录
        this.pending.splice(item, 1);
      }
    }
  };

  /**
   * 参数组装
   * @param isForm 是否以表单形式即application/x-www-form-urlencoded; charset=UTF-8方式
   * @param params 参数
   * @returns {string|{token: string}|*}
   */
  getParams(isForm: boolean, params: any) {
    if (isForm) {
      return qs.stringify(params)
    }
    return params
  }

  /**
   * 开启加载框
   */
  startLoading() {
    if (this.config.startLoading) {
      this.config.startLoading()
    }
  }

  /**
   * 关闭加载框
   */
  endLoading() {
    if (this.config.closeLoading) {
      this.config.closeLoading()
    }
  }

  /**
   * 获取请求头
   */
  getHeaders(isForm: boolean) {
    if (isForm) {
      return {
        'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
        Authorization: this.config.token ? (<tokenFunction>this.config.token)() : ''
      }
    }
    if (!isForm) {
      return {
        'Content-Type': 'application/json; charset=UTF-8',
        Authorization: this.config.token ? (<tokenFunction>this.config.token)() : ''
      }
    }
  }

  errorHandle(status: number, errMsg: string) {
    message.error(errorConfig[status.toString()] ? errorConfig[status.toString()].message : errMsg)
    if (errorConfig[status.toString()] && errorConfig[status.toString()].action) {
      errorConfig[status.toString()].action!()
    }
  }

  baseRequest(method: Method, url: string, params: any, isForm = false) {
    this.startLoading()
    this.axiosRequestNumber = this.axiosRequestNumber + 1
    return new Promise((resolve, reject) => {
      axios({
        url: url,
        method,
        data: method === 'POST' || method === 'PUT' || method === 'PATCH' ? this.getParams(isForm, params) : null,
        params: method === 'GET' || method === 'DELETE' ? this.getParams(isForm, params) : null,
        headers: this.getHeaders(isForm)
      })
        .then((res: AxiosResponse) => {
          if (this.axiosRequestNumber >= 1) {
            this.axiosRequestNumber = this.axiosRequestNumber - 1
          }
          if (this.axiosRequestNumber <= 0) {
            this.axiosRequestNumber = 0
            this.endLoading()
          }
          // 请求结果成功操作
          if (this.config.successCode && this.config.successCode.includes(res.data.code)) {
            resolve(res.data.data)
          } else {
            this.errorHandle(res.data.code, res.data.message)
            // 请求结果失败操作
            reject(res.data.message)
            // 没有权限时的操作例如401
            if (this.config.noAuthCode && this.config.noAuthCode.includes(res.data.code)) {
              if (this.config.callBackNoAuth && this.config.callBackNoAuth instanceof Function) {
                // 无权限的回调操作
                this.config.callBackNoAuth(res.data)
              }
            }
          }
        })
        .catch((err: AxiosError) => {
          message.error('网络错误')
          if (err.toString().indexOf('401') > -1) {
            if (this.config.callBackNoAuth && this.config.callBackNoAuth instanceof Function) {
              // 无权限的回调操作
              this.config.callBackNoAuth(err.toString())
            }
          }
          if (this.axiosRequestNumber >= 1) {
            this.axiosRequestNumber = this.axiosRequestNumber - 1
          }
          if (this.axiosRequestNumber <= 0) {
            this.axiosRequestNumber = 0
            this.endLoading()
          }
          // 错误回调函数
          if (this.config.callBackError && this.config.callBackError instanceof Function) {
            // 错误的回调操作，例如进行错误收集
            this.config.callBackError()
          }
          reject(err)
        })
    })
  }
}

export default HttpRequest
